package com.ndhutu.ee.util.pay.ali;

import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayClient;
import com.alipay.api.CertAlipayRequest;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayFundTransUniTransferModel;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.domain.Participant;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayFundTransUniTransferRequest;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.ndhutu.ee.util.pay.Notify;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * 支付宝支付工具类
 * @Author Shane
 * @Date 2019/10/1812:26
 * @param
 * @return
 **/
public class AliPayUtil {
    private static Logger log = LoggerFactory.getLogger(AliPayUtil.class);

    /**
     * 构造普通公钥模式AlipayClient
     * @param aliPayConfig
     * @return
     * @throws Exception
     */
    public static AlipayClient getAlipayClient(AliPayConfig aliPayConfig) throws Exception{
        AlipayClient alipayClient = new DefaultAlipayClient(aliPayConfig.getGateway(), aliPayConfig.getAppid(),
                aliPayConfig.getAppPrivateKey(),"json", aliPayConfig.getCharset(), aliPayConfig.getAlipayPublicKey(),
                aliPayConfig.getSignType());

        return alipayClient;
    }


    /**
     * 构造证书模式AlipayClient
     * @Author Shane
     * @Date 2019/10/1814:33
     * @param
     * @return
     **/
    public static AlipayClient getCrtAlipayClient(AliPayConfig aliPayConfig) throws Exception {
        CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
        certAlipayRequest.setServerUrl(aliPayConfig.getGateway());
        certAlipayRequest.setAppId(aliPayConfig.getAppid());
        certAlipayRequest.setPrivateKey(aliPayConfig.getAppPrivateKey());
        certAlipayRequest.setFormat("json");
        certAlipayRequest.setCharset(aliPayConfig.getCharset());
        certAlipayRequest.setSignType(aliPayConfig.getSignType());
        certAlipayRequest.setCertPath(aliPayConfig.getAppCertPath());
        certAlipayRequest.setAlipayPublicCertPath(aliPayConfig.getAlipayCertPath());
        certAlipayRequest.setRootCertPath(aliPayConfig.getAlipayRootCertPath());

        AlipayClient alipayClient = new DefaultAlipayClient(certAlipayRequest);

        return alipayClient;
    }

    /**
     * 构造APP支付request
     * @param config
     * @param orderNumber
     * @param amount
     * @param subject
     * @param body
     * @return
     * @throws Exception
     */
    private static AlipayTradeAppPayRequest getAppPayRequest(AliPayConfig config, String orderNumber, Integer amount, String subject, String body) throws Exception{
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        model.setSubject(subject);
        model.setBody(body);
        model.setOutTradeNo(orderNumber);
        model.setTimeoutExpress("30m");
        model.setTotalAmount(new BigDecimal(amount).divide(new BigDecimal("100"), 2, BigDecimal.ROUND_HALF_UP)+"");
        model.setProductCode("QUICK_MSECURITY_PAY");
        request.setBizModel(model);
        request.setNotifyUrl(config.getNotifyUrl());

        return request;
    }

    /**
     * 普通公钥模式APP支付下单
     * @Author Shane
     * @Date 2019/12/11 17:35
     * @param orderNumber   业务订单号
     * @param amount        订单金额，单位分
     * @param subject       商品标题
     * @param body          交易具体描述
     * @return
     **/
    public static String order(AliPayConfig config, String orderNumber, Integer amount, String subject, String body) throws Exception{
        AlipayClient client = getAlipayClient(config);

        AlipayTradeAppPayRequest request = getAppPayRequest(config, orderNumber, amount, subject, body);

        String result = client.sdkExecute(request).getBody();

        log.info("支付宝下单返回结果：" + result);
        return result;
    }

    /**
     * 证书模式APP支付下单
     * @Author Shane
     * @Date 2019/12/11 17:35
     * @param orderNumber   业务订单号
     * @param amount        订单金额，单位分
     * @param subject       商品标题
     * @param body          交易具体描述
     * @return
     **/
    public static String crtOrder(AliPayConfig config, String orderNumber, Integer amount, String subject, String body) throws Exception{
        AlipayClient client = getCrtAlipayClient(config);

        AlipayTradeAppPayRequest request = getAppPayRequest(config, orderNumber, amount, subject, body);

        String result = client.sdkExecute(request).getBody();

        log.info("支付宝下单返回结果：" + result);
        return result;
    }

    /**
     * 支付回调
     * @Author Shane
     * @Date 2019/12/11 18:01
     * @param
     * @return
     **/
    public String notify(HttpServletRequest request, AliPayConfig config, Notify notify) throws Exception{
        Map<String,String> params = new HashMap<String, String>();

        Map requestParams = request.getParameterMap();
        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                        : valueStr + values[i] + ",";
            }
            //乱码解决，这段代码在出现乱码时使用。
            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put(name, valueStr);
        }
        log.info("支付宝支付回调参数："+ JSONObject.toJSONString(params));

        //签名验证
        boolean flag = AlipaySignature.rsaCertCheckV1(params, config.getAlipayCertPath(), config.getCharset(), config.getSignType());

        if(flag){
            if("TRADE_SUCCESS".equals(params.get("trade_status"))) {
                String orderNumber = params.get("out_trade_no");
                Integer amount = (int) (Double.parseDouble(params.get("total_amount")) * 100);

                //统一回调
                notify.handleNotify(orderNumber, amount);
                log.info("订单" + orderNumber + "支付宝回调成功");
            }
            return "success";
        }else{
            return "failure";
        }
    }

    /**
     * 获取回调请求参数
     * @param request
     * @return
     * @throws Exception
     */
    public static Map<String, String> getNotifyRequestParams(HttpServletRequest request) throws Exception{
        Map<String,String> params = new HashMap<String, String>();

        Map requestParams = request.getParameterMap();
        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                        : valueStr + values[i] + ",";
            }
            //乱码解决，这段代码在出现乱码时使用。
            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put(name, valueStr);
        }
        log.info("支付宝支付回调参数："+ JSONObject.toJSONString(params));

        return params;
    }

    /**
     * 校验回调请求
     * @param params    回调请求参数
     * @param config
     * @param keyType   支付宝公钥类型，1-公钥  2-公钥证书
     * @param notify
     * @return
     * @throws Exception
     */
    public static String verifyNotifyRequest(Map<String, String> params, AliPayConfig config, Integer keyType, Notify notify) throws Exception{
        //签名验证
        boolean flag = false;
        if(keyType == 1){
            flag = AlipaySignature.rsaCheckV1 (params ,  config.getAlipayPublicKey() ,  config.getCharset(), config.getSignType());
        }else if(keyType == 2){
            flag = AlipaySignature.rsaCertCheckV1(params, config.getAlipayCertPath(), config.getCharset(), config.getSignType());
        }else{
            log.error("支付宝公钥类型不存在");
        }

        if(flag){
            if("TRADE_SUCCESS".equals(params.get("trade_status"))) {
                String orderNumber = params.get("out_trade_no");
                Integer amount = (int) (Double.parseDouble(params.get("total_amount")) * 100);

                //统一回调
                notify.handleNotify(orderNumber, amount);
                log.info("订单" + orderNumber + "支付宝回调成功");
            }
            return "success";
        }else{
            return "failure";
        }
    }

    /**
     * 打款到支付宝
     * @Author Shane
     * @Date 2019/12/11 18:20
     * @param orderNumber   业务订单号
     * @param amount        打款金额，单位分
     * @param remark        打款描述
     * @param identity      收款方账户
     * @param name          收款方真实姓名
     * @return
     **/
    public boolean trans(AliPayConfig config, String orderNumber, Integer amount, String remark, String identity, String name) throws Exception{
        AlipayClient alipayClient = getCrtAlipayClient(config);

        AlipayFundTransUniTransferRequest alipayRequest = new AlipayFundTransUniTransferRequest();
        AlipayFundTransUniTransferModel model = new AlipayFundTransUniTransferModel();
        model.setOutBizNo(orderNumber);
        model.setTransAmount(new BigDecimal(amount).divide(new BigDecimal("100"), 2, BigDecimal.ROUND_HALF_UP)+"");

        model.setProductCode("TRANS_ACCOUNT_NO_PWD");
        model.setRemark(remark);
        model.setBizScene("DIRECT_TRANSFER");

        Participant payeeInfo = new Participant();
        payeeInfo.setIdentity(identity);
        payeeInfo.setIdentityType("ALIPAY_LOGON_ID");
        payeeInfo.setName(name);
        model.setPayeeInfo(payeeInfo);

        alipayRequest.setBizModel(model);
        String result = alipayClient.certificateExecute(alipayRequest).getBody();

        Map<String, Object> map = JSONObject.parseObject(result);
        Map<String, Object> response = (Map<String, Object>) map.get("alipay_fund_trans_uni_transfer_response");
        if(!("10000".equals(response.get("code")) && "Success".equals(response.get("msg")))){
           return true;
        }else{
            log.error("支付宝打款失败，失败原因：" + response.get("sub_msg"));
            throw new Exception();
        }

    }

}
